package step

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/rlp"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"time"
)

type RequestMpcStepUni struct {
	BaseStep
	messageType      int64
	message          map[discover.NodeID]bool
	peerCount        uint16
	messageToFollow  []byte
	mpcSignByApprove []big.Int
	gpk              []byte
}

func CreateRequestMpcStepUni(totalNum int, pc uint16, messageType int64) *RequestMpcStepUni {

	return &RequestMpcStepUni{
		BaseStep:    *CreateBaseStep(totalNum, totalNum-1),
		messageType: messageType,
		message:     make(map[discover.NodeID]bool),
		peerCount:   pc}
}

func (req *RequestMpcStepUni) InitStep(result mpcprotocol.MpcResultInterface) error {
	log.SyslogDebug("RequestMpcStepUni::InitStep begin", "ctx", req.GetMpcContextId())
	req.BaseStep.InitStep(result)
	if req.messageType == mpcprotocol.MpcSignLeaderUni {
		ret, err := rlp.EncodeToBytes(req.GetMpcContext().GetInput())
		if err != nil {
			log.SyslogErr("RequestMpcStepUni::InitStep", "error",
				err.Error(), "ctx", req.GetMpcContextId())
			return err
		}
		req.messageToFollow = ret

		req.mpcSignByApprove, err = result.GetValue(mpcprotocol.MpcByApprove)
		if err != nil {
			return err
		}
		req.gpk, err = result.GetByteValue(mpcprotocol.MpcGpkBytes)
		if err != nil {
			return err
		}

	}
	return nil
}

func (req *RequestMpcStepUni) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("RequestMpcStepUni::CreateMessage", "ctx", req.GetMpcContextId())
	msg := mpcprotocol.StepMessage{
		MsgCode:   mpcprotocol.RequestMPCUni,
		PeerID:    nil,
		Data:      nil,
		BytesData: nil}

	msg.Data = make([]big.Int, 3)
	msg.Data[0].SetInt64(req.messageType)
	if req.messageType == mpcprotocol.MpcSignLeaderUni {
		msg.Data[1] = req.mpcSignByApprove[0]
		msg.Data[2] = *big.NewInt(0).SetUint64(uint64(req.peerCount))

		msg.BytesData = make([][]byte, 2)
		msg.BytesData[0] = req.gpk
		msg.BytesData[1] = req.messageToFollow

	}
	return []mpcprotocol.StepMessage{msg}
}

func (req *RequestMpcStepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	err := req.BaseStep.FinishStep()
	if err != nil {
		return err
	}

	data := make([]big.Int, 1)
	data[0].SetInt64(req.messageType)
	result.SetValue(mpcprotocol.MPCAction, data)

	// if approved number is less than the threshold, return not enough error ASAP. Add by Jacob begin
	var grpId string
	var input mpcprotocol.SignInput
	gpkBytes := req.gpk

	err = rlp.DecodeBytes(req.messageToFollow, &input)
	if err != nil {
		log.SyslogErr("RequestMpcStepUni::FinishStep", "decode input error", err.Error())
		return err
	}
	grpId, _ = osmconf.GetOsmConf().GetGrpInxByGpk(gpkBytes[:])
	threshNumber, err := osmconf.GetOsmConf().GetThresholdNum(grpId)
	if err != nil {
		log.SyslogErr("RequestMpcStepUni::FinishStep", "Current Number", len(req.message), "threshNumber", threshNumber, "err", err.Error(), "ctx", req.GetMpcContextId())
		return err
	}
	if len(req.message)+1 < int(threshNumber) { // should add leader itself
		log.SyslogErr("RequestMpcStepUni::FinishStep", "Current Number", len(req.message), "threshNumber", threshNumber, "ctx", req.GetMpcContextId())

		nodeIds, _ := osmconf.GetOsmConf().GetPeersNodeIdsByGrpId(grpId)
		var message string
		var notApprovedNodes []discover.NodeID
		for _, nodeId := range nodeIds {
			if _, exist := req.message[nodeId]; !exist {
				//message += nodeId.SlimString() + ","
				if !bytes.Equal(nodeId[:], (*osmconf.GetOsmConf().SelfNodeId)[:]) {
					notApprovedNodes = append(notApprovedNodes, nodeId)
				}
			}
		}
		for index, nodeId := range notApprovedNodes {
			if index == len(notApprovedNodes)-1 {
				message += osmconf.GetOsmConf().GetWorkingAddrByNodeId(&nodeId)
			} else {
				message += osmconf.GetOsmConf().GetWorkingAddrByNodeId(&nodeId) + ","
			}
		}
		return errors.New(fmt.Sprintf("ERROR:%s NotApproved nodes working address:%s", mpcprotocol.ErrTooLessDataApproved, message))
	}
	// Add by Jacob end

	ret := req.GetMpcResult().SetValue(mpcprotocol.ApprovedMpcNumUni, []big.Int{*big.NewInt(0).SetUint64(uint64(len(req.message) + 1))})
	if ret != nil {
		log.SyslogErr("RequestMpcStepUni::FinishStep", "Current Number", "set ApprovedMpcNumUni Error", ret.Error(), "ctx", req.GetMpcContextId())
		return mpcprotocol.ErrSaveValue
	}

	finishTimeBytes, _ := time.Now().MarshalBinary()
	retFinishCollect := req.GetMpcResult().SetByteValue(mpcprotocol.FinishCollectUni, finishTimeBytes)
	if retFinishCollect != nil {
		log.SyslogErr("RequestMpcStepUni::FinishStep", "set FinishCollectUni Error", retFinishCollect.Error(), "ctx", req.GetMpcContextId())
		return mpcprotocol.ErrSaveValue
	}
	return nil
}

func (req *RequestMpcStepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogInfo("RequestMpcStepUni::HandleMessage", "RequestMpcStep.HandleMessage begin, peerID", msg.PeerID.SlimString(), "ctx", req.GetMpcContextId())
	_, exist := req.message[*msg.PeerID]
	if exist {
		log.SyslogErr("RequestMpcStepUni::HandleMessage", "RequestMpcStep.HandleMessage, get message from peerID fail. peer",
			msg.PeerID.SlimString(), "ctx", req.GetMpcContextId())
		return false
	}

	req.message[*msg.PeerID] = true
	return true
}
